﻿using System;
using System.Configuration;
using System.Data.SqlClient;
using System.Text;
using System.Xml;


/// <summary>
/// Exception that is thrown when a population's data is invalid
/// </summary>
public class PopulationInvalidException : ApplicationException
{
    public PopulationInvalidException(string msg)
        : base(msg)
    {
    }
}


/// <summary>
/// Summary description for Population
/// </summary>
public class Population
{
    string id;
    string location, coupleStatus, ethnicity, compare;

    double? value;
    string error;

	private Population()
	{
        value = null;
        error = null;
	}

    /// <summary>
    /// Creates a new Population from a [Population] XmlElement
    /// </summary>
    /// <param name="e">The element to get data from.  Expects subelements of
    /// Location, CoupleStatus, Ethnicity, and Compare</param>
    public Population(XmlElement e)
    {
        this.id = e.GetAttribute("id");
        this.location = GetNodeText(e, "Location");
        this.coupleStatus = GetNodeText(e, "CoupleStatus");
        this.ethnicity = GetNodeText(e, "Ethnicity");
        this.compare = GetNodeText(e, "Compare");

        if (this.location != null)
            this.location = this.location.ToUpperInvariant();
        if (this.coupleStatus != null)
            this.coupleStatus = this.coupleStatus.ToUpperInvariant();
        if (this.compare != null)
            this.compare = this.compare.ToUpperInvariant();
        if (this.ethnicity != null)
            this.ethnicity = this.ethnicity.ToUpperInvariant();

        // save validation for Process()
        //Validate();
    }

    /// <summary>
    /// Gets the comparison type to perform
    /// </summary>
    public string Comparison
    {
        get { return compare; }
    }

    /// <summary>
    /// Gets the couple type data is requested from
    /// </summary>
    public string CoupleStatus
    {
        get { return coupleStatus; }
    }

    public string DbComparison(string comp)
    {
        switch (comp)
        {
            case "AGE":
                return "AvgAge";
            case "INC":
                return "AvgIncome";
            case "VET":
                return "Veteran";
            case "POP":
                return "PopPct";
            case "EDU":
                return "Education";
            case "CHI":
                return "Children";
            default:
                return String.Empty;
        }
    }

    /// <summary>
    /// Gets any errors that may have occurred in processing this population
    /// </summary>
    public string Error
    {
        get { return error; }
    }

    /// <summary>
    /// Gets the ethnicity to select for in this population
    /// </summary>
    public string Ethnicity
    {
        get { return ethnicity; }
    }

    /// <summary>
    /// Gets the first child (text node) of the given child of the parent
    /// </summary>
    /// <param name="parent">The XmlElement containing the node to get the text of</param>
    /// <param name="nodeName">The name of the node to retrieve text for</param>
    /// <returns>The text, or null if the node is not found or has no text node child</returns>
    private string GetNodeText(XmlElement parent, string nodeName)
    {
        XmlNodeList list = parent.GetElementsByTagName(nodeName);
        if (list.Count == 0)
            return null;
        if (list[0].FirstChild == null)
            return null;
        return list[0].FirstChild.Value;
    }

    /// <summary>
    /// Gets the client-assigned ID of this population
    /// </summary>
    public string Id
    {
        get { return id; }
    }

    /// <summary>
    /// Determines if the given value exists in the array
    /// </summary>
    /// <param name="arr">The array to search</param>
    /// <param name="value">The value to search for</param>
    /// <returns>True if the array contains value, false otherwise, null if either argument is null</returns>
    private bool InArray(string[] arr, string value)
    {
        if (value == null || arr == null)
            return false;

        foreach (string val in arr)
        {
            if (val == value)
                return true;
        }
        return false;
    }

    /// <summary>
    /// Gets the location of the population to query
    /// </summary>
    public string Location
    {
        get { return location; }
    }

    /// <summary>
    /// Performs the query associated with this population
    /// </summary>
    public void Process()
    {
        try
        {
            Validate();

            /*ACSData data = new ACSData();
            ACSDataTableAdapters.acs2007TableAdapter adap = new ACSDataTableAdapters.acs2007TableAdapter();
            adap.Fill(data.acs2007);
            ACSData.acs2007Row row = data.acs2007.FindByStateRaceCouple(Location, Ethnicity, CoupleStatus);*/

            SqlConnection conn = Database.Connection;
            SqlCommand cmd = new SqlCommand("GetValue", conn);
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@table_name", "acs2007");
            cmd.Parameters.AddWithValue("@state", Location);
            cmd.Parameters.AddWithValue("@race", Ethnicity);
            cmd.Parameters.AddWithValue("@couple", CoupleStatus);
            cmd.Parameters.AddWithValue("@field", DbComparison(Comparison));
            object ret = cmd.ExecuteScalar();

            if (ret != null && ret != DBNull.Value)
            {
                value = (double)ret;
                error = String.Empty;
            }
            else
            {
                value = null;
                error = "Not found";
            }

            /*if (row != null)
            {
                switch (Comparison)
                {
                    case "AGE":
                        if (row.IsAvgAgeNull())
                            value = null;
                        else
                            value = row.AvgAge;
                        break;
                    case "POP":
                        if (row.IsPopPctNull())
                            value = null;
                        else
                            value = row.PopPct;
                        break;
                    case "INC":
                        if (row.IsAvgIncomeNull())
                            value = null;
                        else
                            value = row.AvgIncome;
                        break;
                    case "VET":
                        if (row.IsVeteranNull())
                            value = null;
                        else
                            value = row.Veteran;
                        break;
                    case "CHI":
                        if (row.IsChildrenNull())
                            value = null;
                        else
                            value = row.Children;
                        break;
                    case "EDU":
                        if (row.IsEducationNull())
                            value = null;
                        else
                            value = row.Education;
                        break;
                    default:
                        throw new PopulationInvalidException("Invalid comparison: " + Comparison);
                }
            }
            else
            {
                value = null;
                error = "Not found";
            }*/
        }
        catch (PopulationInvalidException e)
        {
            value = null;
            error = e.Message;
        }
        catch (Exception e)
        {
            // we don't want to pass along an exception to the user... we want to return
            // a nicely-formatted XML document containing the error
            value = null;
            error = e.GetType().Name + ": " + e.Message;
        }
    }

    /// <summary>
    /// Converts this population into an XML response
    /// </summary>
    /// <param name="doc">The document that the response will be added to</param>
    /// <returns>The root XmlElement of the new population element</returns>
    public XmlElement ToXML(XmlDocument doc)
    {
        XmlElement pop = doc.CreateElement("Population");
        pop.SetAttribute("id", Id);

        XmlElement val = doc.CreateElement("Value");
        if (Value != null)
            val.AppendChild(doc.CreateTextNode(((double)Value).ToString()));
        pop.AppendChild(val);

        XmlElement err = doc.CreateElement("Error");
        if (Error != null && Error != String.Empty)
            err.AppendChild(doc.CreateTextNode(Error));
        pop.AppendChild(err);

        return pop;
    }

    /// <summary>
    /// Validates the parameters of this Population.
    /// Throws a PopulationInvalidException if any of the conditions are not met.
    /// </summary>
    public void Validate()
    {
        string[] validLocs = new string[] {
            "AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA",
            "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
            "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
            "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",
            "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY",
            "DC", "US"};

        string[] validCoupleStatus = new string[] {
            "SSA", "SSM", "SSF", "DSU", "DSM"
        };

        string[] validComparisons = new string[] {
            "AGE", "POP", "INC", "VET", "CHI", "EDU"
        };

        string[] validEthnicities = new string[] {
            "W", "L", "AA", "API", "ANAI", "ALL"
        };

        if (!InArray(validLocs, Location))
            throw new PopulationInvalidException("Invalid location: " + Location);
        if (!InArray(validCoupleStatus, CoupleStatus))
            throw new PopulationInvalidException("Invalid couple status: " + CoupleStatus);
        if (!InArray(validComparisons, Comparison))
            throw new PopulationInvalidException("Invalid comparison: " + Comparison);
        if (!InArray(validEthnicities, Ethnicity))
            throw new PopulationInvalidException("Invalid ethnicity: " + Ethnicity);
    }

    /// <summary>
    /// Gets the value of this Population's query, or null if it is invalid or has
    /// not yet been processed
    /// </summary>
    public double? Value
    {
        get { return value; }
        set { this.value = value; }
    }
}
